bean的作用域 在 Spring 中,可以在<bean>
元素的 scope 属性里设置 bean 的作用域,以决定这个 bean 是单实例的还是多实例的。默认情况下,Spring 只为每个在 IOC 容器里声明的 bean 创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的 getBean() 调用和 bean 引用都将返回这个唯一的 bean 实例。该作用域被称为 singleton,它是所有 bean 的默认作用域。
当bean的作用域为单例时,Spring 会在IOC 容器对象创建时就创建 bean 的对象实例,也就是说会在 new ClassPathXmlApplicationContext
的时候就创建。而当 bean 的作用域为prototype 时,IOC 容器在获取 bean 的实例时创建 bean 的实例对象,也就是会在调用 getBean 方法 applicationContext.getBean("student", Student.class);
时才会创建。
bean 的生命周期
通过构造器或工厂方法创建 bean 实例
为 bean 的属性设置值和对其他 bean 的引用 (依赖注入)
调用 bean 的初始化方法
bean 可以使用了
当容器关闭时,调用 bean 的销毁方法
测试生命周期 创建 Person 类,在其中自定义 init 和 destory 方法。并在合适的位置进行内容打印输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.itguigu.ioc.life;public class Person { private Integer id; private String sex; private String name; public Integer getId () { return id; } public void setId (Integer id) { System.out.println("bean的生命周期-2: 为bean的属性设置值" ); this .id = id; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Person (Integer id, String sex, String name) { super (); this .id = id; this .sex = sex; this .name = name; } public Person () { super (); System.out.println("bean的生命周期-1: 通过构造器或工厂方法创建 bean 实例" ); } @Override public String toString () { System.out.println("bean的生命周期-4: 使用 bean" ); return "Person [id=" + id + ", sex=" + sex + ", name=" + name + "]" ; } public void init () { System.out.println("bean的生命周期-3: 初始化方法" ); } public void destory () { System.out.println("bean的生命周期-5: 调用bean的销毁方法" ); } }
创建 life.xml, 进行配置,并指定 init-method
方法和 destroy-method
方法
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="person" class ="com.itguigu.ioc.life.Person" init-method ="init" destroy-method ="destory" > <property name ="id" value ="10001" > </property > <property name ="sex" value ="男" > </property > <property name ="name" value ="张三" > </property > </bean > </beans >
运行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.itguigu.ioc.life;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test { public static void main (String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("life.xml" ); Person person = applicationContext.getBean("person" , Person.class ) ; System.out.println(person); applicationContext.close(); } }
bean 的后置处理器 bean后置处理器允许在调用初始化方法前后 对bean进行额外的处理。也就是说如果加上 bean 的后置处理器,那么 bean 的生命周期由以前的五步变成七步 。
通过构造器或工厂方法创建 bean 实例
为bean的属性设置值 和对其他bean的引用 (依赖注入)
将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
调用bean的初始化 方法
将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
bean可以使用了
当容器关闭时调用bean的销毁方法
创建类 AfterHandler 并实现 BeanPostProcessor 接口,让其成为一个后置处理器。并在其中修改相关 bean 信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.itguigu.ioc.life;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;public class AfterHandler implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { Person person = (Person) bean; person.setName("这是我在后置处理器中修改的名字" ); return person; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { return bean; } }
在 xml 中引用这个后置处理器
1 2 <bean class ="com.itguigu.ioc.life.AfterHandler" > </bean >
再次运行测试,这时就能看到我们在后置处理器中对 Person 对象修改的结果了,name 变成了 “这是我在后置处理器中修改的名字”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.itguigu.ioc.life;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test { public static void main (String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("life.xml" ); Person person = applicationContext.getBean("person" , Person.class ) ; System.out.println(person); applicationContext.close(); } }
使用外部的属性文件 当 bean 的配置信息逐渐增多时,查找和修改一些 bean 的配置信息就变得愈加困难。这时可以将一部分信息提取到 bean 配置文件的外部,以 properties 格式的属性文件保存起来,同时在 bean 的配置文件中引用 properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改 properties 属性文件即可。这种技术多用于连接数据库的基本信息的配置。
不使用外部属性文件 1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="datasource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/bookstore" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="123456" > </property > </bean > </beans >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.itguigu.ioc.datasource;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidPooledConnection;public class Test { public static void main (String[] args) throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("datasource.xml" ); DruidDataSource dataSource = applicationContext.getBean("datasource" , DruidDataSource.class ) ; System.out.println(dataSource); DruidPooledConnection connection = dataSource.getConnection(); System.out.println(connection); } }
使用外部属性文件 将资源文件放在 config 文件下,命名为 db.properties
1 2 3 4 jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/bookstore jdbc.username = root jdbc.password = 123456
加载外部资源文件
1 2 3 4 <bean class ="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name ="location" value ="db.properties" > </property > </bean >
1 2 <context:property-placeholder location ="db.properties" />
加载资源后就可以使用
1 2 3 4 5 6 7 <bean id ="datasource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="${jdbc.driver}" > </property > <property name ="url" value ="${jdbc.url}" > </property > <property name ="username" value ="${jdbc.username}" > </property > <property name ="password" value ="${jdbc.password}" > </property > </bean >
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.itguigu.ioc.datasource;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidPooledConnection;public class Test { public static void main (String[] args) throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("datasource.xml" ); DruidDataSource dataSource = applicationContext.getBean("datasource" , DruidDataSource.class ) ; System.out.println(dataSource); DruidPooledConnection connection = dataSource.getConnection(); System.out.println(connection); } }
自动装配(自动注入) 手动装配:以value或ref的方式明确指定属性值 都是手动装配。 自动装配:根据指定的装配规则,不需要明确指定 ,Spring 自动 将匹配的属性值注入 bean 中。(也就是非自面量的属性,即需要使用 ref 的)
自动装配使用 autowire ,它可以根据某种策略自动为非字面量属性赋值。autowire 共有 5 种方式,这里介绍 byName 和 byType
创建三个类,分别是 Car,Dept,Emp。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.itguigu.ioc.auto;public class Car { private Integer cid; private String cname; public Integer getCid () { return cid; } public void setCid (Integer cid) { this .cid = cid; } public String getCname () { return cname; } public void setCname (String cname) { this .cname = cname; } @Override public String toString () { return "Car [cid=" + cid + ", cname=" + cname + "]" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.itguigu.ioc.auto;public class Dept { private Integer did; private String dname; public Integer getDid () { return did; } public void setDid (Integer did) { this .did = did; } public String getDname () { return dname; } public void setDname (String dname) { this .dname = dname; } @Override public String toString () { return "Dept [did=" + did + ", dname=" + dname + "]" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.itguigu.ioc.auto;public class Emp { private Integer eid; private String ename; private Car car; private Dept dept; public Integer getEid () { return eid; } public void setEid (Integer eid) { this .eid = eid; } public String getEname () { return ename; } public void setEname (String ename) { this .ename = ename; } public Car getCar () { return car; } public void setCar (Car car) { this .car = car; } public Dept getDept () { return dept; } public void setDept (Dept dept) { this .dept = dept; } @Override public String toString () { return "Emp [eid=" + eid + ", ename=" + ename + ", car=" + car + ", dept=" + dept + "]" ; } }
以前的手动装配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <bean id ="emp" class ="com.itguigu.ioc.auto.Emp" > <property name ="eid" value ="1001" > </property > <property name ="ename" value ="zhangsan" > </property > <property name ="car" ref ="car" > </property > <property name ="dept" ref ="dept" > </property > </bean > <bean id ="car" class ="com.itguigu.ioc.auto.Car" > <property name ="cid" value ="66666" > </property > <property name ="cname" value ="二手奥拓" > </property > </bean > <bean id ="dept" class ="com.itguigu.ioc.auto.Dept" > <property name ="did" value ="22202" > </property > <property name ="dname" value ="市场运营" > </property > </bean >
根据名称自动装配
这里演示的是 byName, 即 bean 的 id 和属性名一致就能自动装配,所以下面 property 省略了car 和 dept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <bean id ="emp" class ="com.itguigu.ioc.auto.Emp" autowire ="byName" > <property name ="eid" value ="1001" > </property > <property name ="ename" value ="zhangsan" > </property > </bean > <bean id ="car" class ="com.itguigu.ioc.auto.Car" > <property name ="cid" value ="66666" > </property > <property name ="cname" value ="二手奥拓" > </property > </bean > <bean id ="dept" class ="com.itguigu.ioc.auto.Dept" > <property name ="did" value ="22202" > </property > <property name ="dname" value ="市场运营" > </property > </bean >
根据类型自动装配
bean 的类型和属性类型一致就能自动装配。甚至 xml 里面可以用子类给父类赋值,也可以用接口实现类给接口赋值。但是要注意,在 Spring 容器中只能存在一个为之匹配赋值的对象,也就是说想用自动装配,那么 Spring 容器中 Car 和 Dept 都只能有一个。不然会和使用类型获取 bean 的 getBean 方法发生同样的错误,NoUniqueBeanDefinitionException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <bean id ="emp" class ="com.itguigu.ioc.auto.Emp" autowire ="byType" > <property name ="eid" value ="1001" > </property > <property name ="ename" value ="zhangsan" > </property > </bean > <bean id ="car1" class ="com.itguigu.ioc.auto.Car" > <property name ="cid" value ="66666" > </property > <property name ="cname" value ="二手奥拓" > </property > </bean > <bean id ="dept1" class ="com.itguigu.ioc.auto.Dept" > <property name ="did" value ="22202" > </property > <property name ="dname" value ="市场运营" > </property > </bean >
byName 和 byType 选用建议: byName 和 byType都有自己的缺点,一个是依赖名称相同,另一个依赖类型相同,且 autowire 会作用于该 bean 的所有非字面量属性,即限制不了不想让某个非字面量属性自动赋值。所以,相对于使用注解的方式实现的自动装配,在 XML 文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
代码地址